<?php
/**
 * 内存与执行时间分析
 *
 * @author Zeon <scplay@gmail.com>
 * @date 2018/8/28 9:20
 * @copyright PanxSoft Inc.
 */

namespace App\Services\Utils;


/**
 * @example
        Measure::start();
        Measure::mark('A');
        Measure::mark('B');
        ...
        Measure::mark('N');
        Measure::end();
 *
 * Class Measure
 * @package App\Services\Utils
 */
class Measure
{
    const PREFIX = '[Measure] ';
    const PREFIX_START = '[Measure Start] ';
    const PREFIX_END = '[Measure End] ';
    const PREFIX_MARK = '[Measure Mark] ';
    const PREFIX_MEM = '[Measure Memory] ';

    const AS_FLOAT = true;
    private static $start_ms;
    private static $end_ms;

    public static function start(): void
    {
        self::initStartTime();
        logger(self::PREFIX_START . '(0s)'. self::memoryUsage());
    }

    public static function mark(string $tag = null): void
    {
        self::initStartTime();
        $now = self::floatTime();
        $elapse = $now - self::$start_ms;
        logger(self::PREFIX_MARK . $tag . self::formatElapse($elapse). self::memoryUsage());
//        self::printCaller();
    }

    private static function memoryUsage(): string
    {
        $mem_use = self::humanSizable(memory_get_usage());
        $mem_peak = self::humanSizable(memory_get_peak_usage());

        return " usage: $mem_use, peak: $mem_peak";
    }

    public static function end(): void
    {
        self::initStartTime();
        self::$end_ms = self::floatTime();
        $elapse = self::$end_ms - self::$start_ms;

        logger(self::PREFIX_END. self::formatElapse($elapse). self::memoryUsage());
    }

    /**
     * @param $bytes
     * @param int $decimals
     *
     * @return string
     */
    private static function humanSizable($bytes, $decimals = 2): string
    {
        $size = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
        $factor = (int) floor((strlen($bytes) - 1) / 3);
        $capacity = $size[$factor] ?? '?B';

        return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . $capacity;
    }

    /**
     * @return mixed
     */
    private static function floatTime()
    {
        return microtime(self::AS_FLOAT);
    }

    /**
     * @param $elapse
     *
     * @return string
     */
    private static function formatElapse(float $elapse): string
    {
        $formatElapse = number_format($elapse, '4', '.', '') . 's';

        return '('.$formatElapse.')';
    }

    private static function printCaller()
    {
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);

        logger($trace[1]['file']. ':' .$trace[1]['line']);
    }

    private static function initStartTime(): void
    {
        if (! self::$start_ms) {
            self::$start_ms = self::floatTime();
        }
    }
}